package com.gzz.redis.cachedata;

import com.gzz.redis.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

@Aspect
@Slf4j
public class CacheAop {
    @Resource
    RedisToolUtil redisUtil;
    @Around(value = "@annotation(com.wpr.common.annotation.CacheClear)")
    public Object cacheClear(ProceedingJoinPoint pjp) throws Throwable {
        log.info("----------------缓存清除AOP开始--------------------");
        // 执行业务
        Object obj = pjp.proceed();
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取注解
        //获取Method对象
        Method method = signature.getMethod();
        CacheClear cacheClear = method.getAnnotation(CacheClear.class);
        // 如果没有注解则直接返回
        if(null == cacheClear) {
            return obj;
        }
        // 缓存key
        String[] keyNames = cacheClear.keyName();
        if(ObjectUtils.isEmpty(keyNames)){
            keyNames = new String[] {pjp.getSignature().getDeclaringType().getName() + pjp.getSignature().getName()};
        }
        if(ObjectUtils.isEmpty(keyNames)) {
            return obj;
        }
        try {
            // 获取redis工具类
            //RedisUtil redisUtil = (RedisUtil) SpringContextHolder.getBean(RedisUtil.class.getName());
            Arrays.stream(keyNames).forEach(redisKey -> {
                log.info("清除缓存的KEY:"+redisKey);
                // 删除缓存
                redisUtil.deleteByPattern(redisKey+"*");
            });
        } catch (Exception e) {
            log.error("清除缓存报错:"+e.getMessage());
            log.error("清除缓存报错:"+e);
        }

        log.info("----------------缓存清除AOP结束--------------------");
        return obj;
    }

    @Around(value = "@annotation(com.wpr.common.annotation.CacheData)")
    public Object aroundCacheData(ProceedingJoinPoint pjp) throws Throwable {
        log.info("----------------缓存AOP执行开始--------------------");
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取注解
        //获取Method对象
        Method method = signature.getMethod();
        CacheData cacheData = method.getAnnotation(CacheData.class);
        // 如果没有注解则直接返回
        if(null == cacheData) return pjp.proceed();
        // 缓存key
        String keyName = cacheData.keyName();
        if(StringUtils.isEmpty(keyName)){
            log.info("目标方法所属类的名:" + pjp.getSignature().getDeclaringType().getName());
            log.info("目标方法名:" + pjp.getSignature().getName());
            keyName = pjp.getSignature().getDeclaringType().getName() + pjp.getSignature().getName();
        }
        log.info("传参缓存KEY:"+keyName);
        // 缓存时间
        long cacheTime = cacheData.cacheTime();
        log.info("传参缓存时间:"+cacheTime+"秒");
        // 参数
        Object[] args = pjp.getArgs();
        // 获取参数下标
        int[] paramIndex = cacheData.paramIndex();
        // 拼装缓存KEY
        AtomicReference<String> redisKey = new AtomicReference<>(keyName);
        if(!getRedisKey(args, keyName, paramIndex, redisKey)){
            return pjp.proceed();
        }

        Object obj = null;
        try {
            // 获取redis工具类
            //RedisUtil redisUtil = (RedisUtil) SpringContextHolder.getBean(RedisUtil.class.getName());
            // 从缓存中获取数据
            obj = redisUtil.get(redisKey.get());
            if (null == obj) {
                obj = pjp.proceed();
                redisUtil.set(redisKey.get(), obj, cacheTime);
            }
        } catch (SerializationException e){
            log.error("缓存执行报错: 方法返回对象需实现接口Serialization!");
            obj = pjp.proceed();
        } catch (Throwable throwable) {
            log.error("缓存执行报错:"+throwable.getMessage());
            log.error("缓存执行报错:"+throwable);
            obj = pjp.proceed();
        }
        log.info("----------------缓存AOP执行结束--------------------");
        return obj;
    }

    /**
     * 拼接redisKey
     */
    public boolean getRedisKey(Object[] args, String keyName, int[] paramIndex, AtomicReference<String> redisKey) {
        boolean flag = true;
        if (!ObjectUtils.isEmpty(args)) {
            if(!ObjectUtils.isEmpty(paramIndex)){
                /* paramIndex的元素不能大于args的size */
                Object[] argsNew = new Object[paramIndex.length];
                try {
                    for(int i = 0;i < paramIndex.length;i++){
                        int index = paramIndex[0];
                        Assert.isTrue(index < args.length,"paramIndex存在参数下标大于方法参数个数");
                        argsNew[i] = args[i];
                    }
                    flag = setRedisKey(argsNew, redisKey);
                } catch (Exception e) {
                    log.error("拼接自定义参数序列化报错:"+e.getMessage());
                    log.error("拼接自定义参数序列化报错:"+e);
                    flag = false;
                }
            }else {
                flag = setRedisKey(args, redisKey);
            }
        }
        return flag;
    }

    public boolean setRedisKey(Object[] args, AtomicReference<String> redisKey) {
        boolean flag = true;
        try {
            // JSON.toJSONString(args);
            String paramJson = "";
            log.info("方法参数:"+paramJson);
            String key = redisKey.get() + ":" + paramJson;
            redisKey.set(key);
        } catch (Throwable e) {
            log.error("方法参数传JSON,报错信息:"+e.getMessage());
            log.error("方法参数传JSON,报错堆栈信息:"+e);
            flag = false;
        }
        return flag;
    }
}